<?php
class Node_model_node extends Model {
    private $table_node = 'node';
    private $table_type = 'type';
    private $table_fieldtype = 'fieldtype';
    private $table_type_field = 'type_field';
    private $table_keyword = 'keyword';
    private $table_node_keyword = 'node_keyword';
    private $table_node_mapping = 'node_mapping';
    private $table_node_position = 'node_position';
    private $table_image = 'image';
    
    private $types;
    private $fieldtypes;
    private $type_fields = array();
    
    public function get_types() {
        if ( ! isset($this->types)) {
            $this->types = $this->db->select_rows(
                $this->table_type,
                array('id', 'name', 'has_content')
            );
        }
        
        return $this->types;
    }
    
    
    public function get_fieldtypes() {
        if ( ! isset($this->fieldtypes)) {
            $this->fieldtypes = $this->db->select_rows(
                $this->table_fieldtype,
                array('id', 'name', 'db_data_type')
            );
        }
        return $this->fieldtypes;
    }
    
    
    public function get_type_fields($type_id) {
        $type_id = (int) $type_id;
        if ($type_id <= 0) {
            tos_die();
        }
        if ( ! isset($this->type_fields[$type_id])) {
            $sql = "SELECT `t`.`name`, `t`.`required`, `t`.`default`, `t`.`has_default`, `t`.`is_description`, `f`.`name` `fieldtype_name`, `f`.`db_data_type` " . 
                "FROM `{$this->table_type_field}` `t` INNER JOIN `{$this->table_fieldtype}` `f` " .
                "ON `t`.`fieldtype_id` = `f`.`id` " .
                "WHERE `t`.`type_id` = {$type_id} ";
            //echo $sql;
            $rows = $this->db->rows($sql);
            //print_r($rows);
            if ( ! $rows) {
                return false;
            }
            $this->type_fields[$type_id] = $rows;
        }
        
        return $this->type_fields[$type_id];
        
    }
    
    
    public function get_entry($node_id) {
        return $this->db->select_row(
            $this->table_node,
            array(),
            array('id' => $node_id)
        );
    }
    
    
    public function get_entries($keyword_str = '', $prev_id = 0, $next_id = 0, $type_id, $limit = 10, $offset = 0) {
        $keyword_str = (string) $keyword_str;
        $keyword_ids = array();
        if  ( ! empty($keyword_str)) {
            $keyword_id = $this->_has_keyword($keyword_str);
            $k_arr = tos_explode_keywords($keyword_str, ' ');
            $keyword_ids = $this->_get_keyword_ids($k_arr);
        }
        
        return $this->_search_entries($keyword_ids, $prev_id, $next_id, $type_id, $limit, $offset);
        
        
        
        $rows = $this->db->select_rows(
            $this->table_node,
            array()
        );
        return $rows;
    }
    
    
    public function get_content_entry($node_id, $table) {
        return $this->db->select_row(
            $table,
            array(),
            array('node_id' => $node_id)
        );
    }
    
    
    public function get_node_keywords($node_id) {
        $node_id = (int) $node_id;
        if ($node_id <= 0) tos_die();
        $sql = "SELECT `k`.`id`, `n`.`hit`, `n`.`prev_hit`, `n`.`next_hit`, " .
         "`k`.`word` `keyword`, `k`.`color_id` " . 
         "FROM `{$this->table_node_keyword}` `n` INNER JOIN `{$this->table_keyword}` `k` ON `n`.`keyword_id` = `k`.`id` " . 
         "WHERE `n`.`node_id`={$node_id}";
        
        return $this->db->rows($sql, 'node_id');
    }
    
    
    public function get_prev_node_mapping($node_id) {
        $node_id = (int) $node_id;
        if ($node_id <= 0) tos_die();
        
        $sql = "SELECT `n`.*, `nm`.`keyword_id` FROM `{$this->table_node_mapping}` `nm` " .
            "LEFT JOIN `{$this->table_node}` `n` ON `nm`.`prev_id`=`n`.`id` " .
            "WHERE `nm`.`next_id`={$node_id}";
        return $this->db->rows($sql);
    }
    
    
    public function edit_node($node_id = 0, $caption = '', $description = '', 
        $url_segment = '', $is_public = 0, $thumbnail = ''
        ) {
        $node_id = (int) $node_id;
        if ($node_id <= 0) tos_die();
        
        $caption = (string) $caption;
        $caption = trim($caption);
        $description = trim($description);
        $url_segment = trim($url_segment);
        $is_public = (0 == (int) $is_public) ? 0 : 1;
        $thumbnail = trim($thumbnail);
        //$now = time();
        
        $data = array();
        $data_format = array();
        
        if ( ! empty($caption)) {
            $data['caption'] = $caption;
        }
        if ( ! empty($url_segment)) {
            $data['url_segment'] = $url_segment;
        }
        if ( ! empty($is_public)) {
            $data['is_public'] = $is_public;
            $data_format['is_public'] = DB_DATA_INT;
        }
        
        if ( ! empty($thumbnail)) {
            $data['thumbnail'] = $thumbnail;
        }
        if ( ! empty($description)) {
            $data['description'] = $description;
        }
        
        if ($data) {
            $data['changed'] = time();
            $data_format['changed'] = DB_DATA_INT;
            return $this->db->update(
                $this->table_node, 
                $data, 
                array('id' => $node_id),
                $data_format,
                array('id', DB_DATA_INT)
            );
        }
        return true;
        
    }
    
    
    public function add_node($user_id = 0, $type_id = 0, $caption = '', $keywords = array(), 
        $description = '', $url_segment = '', $is_public = 0, $thumbnail = ''
    ) {
        $user_id = (int) $user_id;
        if ($user_id <= 0) tos_die('user_id cannot be less than 1');
        
        $type_id = (int) $type_id;
        if ($type_id <= 0) tos_die('type_id cannot be less than 1');
        
        $caption = trim($caption);
        if (empty($caption)) tos_die('caption cannot be empty');
        
        $description = trim($description);
        $url_segment = trim($url_segment);
        $url_segmet = empty($url_segment) ? tos_clean_url_segment($caption) : tos_clean_url_segment($url_segment);
        $is_public = (0 == (int) $is_public) ? 0 : 1;
        $thumbnail = trim($thumbnail);
        
        $now = time();
        $keyword_str = '';
        if (is_array($keywords)) {
            $keyword_str = implode(',', $keywords);
        }
        
        
        $data = array(
                'user_id'       => $user_id,
                'type_id'       => $type_id,
                'caption'       => $caption,
                'keywords'      => $keyword_str,
                'description'   => $description,
                'url_segment'   => $url_segment,
                'is_public'   => $is_public,
                'created'       => $now,
                'changed'       => $now,
                'thumbnail' => $thumbnail
            );
        $data_format = array(
                'user_id'       => DB_DATA_INT,
                'type_id'       => DB_DATA_INT,
                'created'       => DB_DATA_INT,
                'changed'       => DB_DATA_INT,
                'is_public'     => DB_DATA_INT
            );
            
        $node_id = $this->db->insert($this->table_node, $data, $data_format);
        
        if ($node_id < 0) {
            tos_die('add node failed');
        }
        
        //$new_keywords = array();
        if (is_array($keywords)) {
            foreach ($keywords as &$keyword) {
                $keyword = tos_clean_keyword($keyword);
            }
        }
        
        $caption_keywords = tos_explode_keywords($caption, ' ');
        
        if ($keywords && $caption_keywords) {
            $keywords = array_merge($keywords, $caption_keywords);
            $keywords = array_unique($keywords);
        } else if ($caption_keywords) {
            $keywords = $caption_keywords;
        } 
        
        if ($keywords) {
            $this->add_keywords($node_id, $keywords);
        }
        
        return $node_id;
    }
    

    
    
    
    public function add_keywords($node_id = 0, $keywords = array()) {
        $node_id = (int) $node_id;
        if ($node_id <= 0) tos_die('node id error');
        
        $keywords = (array) $keywords;
        foreach ($keywords as $keyword) {
            if ( ! empty($keyword)) {
                $keyword_id = $this->_has_keyword($keyword);
                if ($keyword_id <= 0)
                    $keyword_id = $this->_add_keyword($keyword);
                $kn_id = $this->_has_kn($keyword_id, $node_id);
                if ($kn_id <= 0)
                    $kn_id = $this->_add_kn($keyword_id, $node_id);
            }
        }
    }
    
    
    public function add_content($table, $data, $data_format = array()) {
        $table = trim($table);
        if (empty($table)) tos_die();
        
        return $this->db->insert(
            $table, $data, $data_format
        );
    }
    
    
    public function edit_content($table, $node_id, $data, $data_format = array()) {
        $table = trim($table);
        if (empty($table)) tos_die();
        $node_id = (int) $node_id;
        if ($node_id <= 0) tos_die();
        
        return $this->db->update(
            $table, $data, 
            array('node_id' => $node_id),
            $data_format,
            array('node_id', DB_DATA_INT)
        );
    }
    
    
    public function get_content($table, $node_id) {
        $node_id = (int) $node_id;
        if ($node_id <= 0) tos_die();
        
        $table = (string) $table;
        $table = trim($table);
        if (empty($table)) tos_die();
        
        return $this->db->select_row($table, array(), array('node_id' => $node_id));
    }
    
    
    public function add_node_mapping($prev_id, $next_id, $keyword = '') {
        
        $keyword = (string) $keyword;
        $keyword = trim($keyword);
        
        $keyword_id = 0;
        if ( ! empty($keyword)) {
            $keyword_id = (int) $this->_has_keyword($keyword);
            if ($keyword_id <= 0) {
                $keyword_id = $this->_add_keyword($keyword);
            }
        }
        $node_mapping_id = $this->_has_node_mapping($prev_id, $next_id, $keyword_id);
        if ($node_mapping_id <= 0) {
            $node_mapping_id = $this->db->insert(
                $this->table_node_mapping,
                array('prev_id' => $prev_id, 'next_id' => $next_id, 'keyword_id' => $keyword_id),
                array('prev_id' => DB_DATA_INT, 'next_id' => DB_DATA_INT, 'keyword_id' => DB_DATA_INT)
            );
        } else if ($keyword_id > 0) {
            $node_mapping_id = $this->db->update(
                $this->table_node_mapping,
                array('keyword_id' => $keyword_id),
                array('keyword_id' => DB_DATA_INT)
            );
        }
        
    }
    
    // private function start 
    private function _has_node_mapping($prev_id, $next_id, $keyword_id = 0) {
        $prev_id = (int) $prev_id;
        if ($prev_id <= 0) tos_die();
        
        $next_id = (int) $next_id;
        if ($next_id <= 0) tos_die();
        
        $row = $this->db->select_row(
            $this->table_node_mapping,
            array('id'),
            array('prev_id' => $prev_id, 'next_id' => $next_id, 'keyword_id' => $keyword_id),
            array('id' => DB_DATA_INT),
            array('prev_id' => DB_DATA_INT, 'next_id' => DB_DATA_INT, 'keyword_id' => DB_DATA_INT)
        );
        $id = 0;
        if ($row) {
            $id = (int) $row['id'];
            if ($id <= 0) tos_die();
        }
        return $id;
    }
    
    
    private function _has_keyword($keyword = '') {
        static $keywords = array();
        
        $keyword = (string) $keyword;
        $keyword = trim($keyword);
        if (empty($keyword)) tos_die();
        
        if ( ! isset($keywords[$keyword])) {
            $table = $this->table_keyword;
            $row = $this->db->select_row(
                $table,
                array('id'),
                array('word' => $keyword)
            );
            if ($row) {
                $id = (int) $row['id'];
                if ($id <= 0) tos_die();
                $keywords[$keyword] = $id;
            } else {
                return 0;
            }
        }
        return $keywords[$keyword];
    }
    
    private function _add_keyword($keyword = '') {
        $keyword = trim($keyword);
        if (empty($keyword)) {
            tos_die();
        }
        
        $table = $this->table_keyword;
        $id = $this->_has_keyword($keyword);
        if ($id > 0) {
            $this->db->update(
                $table,
                array('hit' => 'hit+1'),
                array('id'  => $id),
                array('hit' => DB_DATA_SYS),
                array('id'  => DB_DATA_INT)
            );
        } else {
            $id = $this->db->insert(
                $table,
                array('word' => $keyword)
            );
        }
        if ($id <= 0) tos_die();
        
        return $id;
    }
    
    
    private function _has_kn($keyword_id, $node_id) {
        static $kns = array();
        
        $keyword_id = (int) $keyword_id;
        if ($keyword_id <= 0) tos_die();
        
        $node_id = (int) $node_id;
        if ($node_id <= 0) tos_die();
        
        $key = $keyword_id . '-' . $node_id;
        if ( ! isset($kns[$key])) {
            $table = $this->table_node_keyword;
            $row = $this->db->select_row(
                $table,
                array('id'),
                array('keyword_id' => $keyword_id, 'node_id' => $node_id),
                array('keyword_id' => DB_DATA_INT, 'node_id' => DB_DATA_INT)
            );
            if ($row) {
                $id = (int) $row['id'];
                if ($id <= 0) tos_die();
                $kns[$key] = $id;
            } else {
                return false;
            }
        }
        return $kns[$key];
        
    }
    
    
    private function _add_kn($keyword_id, $node_id, $hit = KN_HIT_NONE) {
        $id = $this->_has_kn($keyword_id, $node_id);
        $hit = ($hit == KN_HIT_NEXT || $hit == KN_HIT_PREV) ? $hit : KN_HIT_NONE;
        $table = $this->table_node_keyword;
        if ($id > 0) {
            $set_data = array();
            $set_format = array();
            $set_data['hit'] = 'hit+1';
            $set_format['hit'] = DB_DATA_SYS;
            
            if ($hit == KN_HIT_NEXT) {
                $set_data['next_hit'] = 'next_hit+1';
                $set_format['next_hit'] = DB_DATA_SYS;
            } else if ($hit == KN_HIT_PREV) {
                $set_data['prev_hit'] = 'prev_hit+1';
                $set_format['prev_hit'] = DB_DATA_SYS;
            }
            $this->db->update(
                $table,
                $set_data,
                array('id'  => $id),
                $set_format,
                array('id'  => DB_DATA_INT)
            );
        } else {
            $data = array();
            $data_format = array();
            $data['node_id'] = $node_id;
            $data_format['node_id'] = DB_DATA_INT;
            $data['keyword_id'] = $keyword_id;
            $data_format['keyword_id'] = DB_DATA_INT;
            
            if ($hit == KN_HIT_NEXT) {
                $data['next_hit'] = '1';
                $data_format['next_hit'] = DB_DATA_INT;
            } else if ($hit == KN_HIT_PREV) {
                $data['prev_hit'] = '1';
                $data_format['prev_hit'] = DB_DATA_INT;
            }
            $id = $this->db->insert($table, $data, $data_format);
        }
        
        if ($id <= 0) tos_die();
        return $id;
    }
    
    
    private function _get_keyword_ids ($keywords) {
        if (is_array($keywords)) {
            $sql = "SELECT `id` FROM `{$this->table_keyword}` WHERE `word` like '" .
                 implode("%' OR `word` like '", $keywords) . "%'";
            $rows = $this->db->rows($sql);
            $ids = array_keys($rows);
            return $ids;
        }
    }
    
    
    
    private function _search_entries($keyword_ids = array(), $prev_id = 0, $next_id = 0, 
        $type_id = 0, $limit = 10, $offset = 0 ) {
        
        
        $prev_id = (int) $prev_id;
        $next_id = (int) $next_id;
        $type_id = (int) $type_id;
        $limit = (int) $limit;
        $offset = (int) $offset;
        
        $limit = ($limit <= 0) ? 10 : $limit;
        $offset = ($offset <= 0) ? 0 : $offset;
        
        if ($type_id > 0) {
            $sql_type = " AND `n`.`type_id`={$type_id} ";
        }else {
            $sql_type = '';
        }
        
        $sql_limit = "LIMIT {$offset},{$limit}";
        if ($keyword_ids && is_array($keyword_ids)) {
            $sql_keywords = '(' . implode(',', $keyword_ids) . ')';
        } else {
            $sql_keywords = false;
        }
        
        
        
        
        if ($prev_id > 0 || $next_id > 0) {
            if ($prev_id > 0) {
                $inner = "`nm`.`next_id`=`n`.`id`";
                $where = "`nm`.`prev_id`='{$prev_id}'";
            } else {
                $inner = "`nm`.`prev_id`=`n`.`id`";
                $where = "`nm`.`next_id`='{$next_id}'";
            }
            $sql = "SELECT `n`.* FROM `{$this->table_node_mapping}` `nm` " .
                "INNER JOIN `{$this->table_node}` `n` ON {$inner} WHERE {$where}  ";
            
            if ($sql_keywords) {
                $sql .=  "AND `nm`.`keyword_id` IN {$sql_keywords} " ;
            }
            $sql .= $sql_type . $sql_limit;
        } else {
            if ($sql_keywords) {
                $sql = "SELECT `n`.* FROM `{$this->table_node_keyword}` `nk` " .
                    "INNER JOIN `{$this->table_node}` `n` ON `nk`.`node_id` = `n`.`id` ";
                $sql .= "WHERE `nk`.`keyword_id` IN  {$sql_keywords} ";
            }  else {
                $sql = "SELECT `n`.* FROM `{$this->table_node}` `n` WHERE 1 ";
            }
            $sql .= $sql_type . $sql_limit;
                
        }
        //echo $sql;
        return $this->db->rows($sql);
    }
    // private function end
}
?>